package com.damai.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.citywithincity.jobqueue.JobHandler;
import org.citywithincity.jobqueue.TaskQueue;
import org.quartz.Job;
import org.quartz.SchedulerException;

import com.citywithincity.api.ApiFilter;
import com.citywithincity.api.entities.Customer;
import com.citywithincity.api.utils.ApiUtil;
import com.citywithincity.db.Db;
import com.citywithincity.db.drivers.oracle.OracleSqlHelper;
import com.citywithincity.db.meta.ColumnStruct;
import com.citywithincity.db.meta.DbStructFactory;
import com.citywithincity.db.meta.TableStruct;
import com.damai.action.aop.Invoker;
import com.damai.action.aop.impl.WebInterceptor;
import com.damai.action.factories.ApiJsonActionFactory;
import com.damai.action.factories.SimpleJsonApiFactory;
import com.damai.action.factories.WebActionFactory;
import com.damai.action.meta.ApiMetaData;
import com.damai.action.meta.ParamHelperTypes;
import com.damai.action.meta.ParamInfo;
import com.damai.annotation.Action;
import com.damai.annotation.Aop;
import com.damai.annotation.Controller;
import com.damai.annotation.Crypt;
import com.damai.annotation.ReturnString;
import com.damai.annotation.Token;
import com.damai.error.ApiError;
import com.damai.error.ErrorUtil;
import com.damai.excel.ExcelTable;
import com.damai.hotload.App;
import com.damai.hotload.HotLoadException;
import com.damai.jobcenter.QuartzPlugin;
import com.damai.js.impl.JsInvoker;
import com.damai.lang.Lang;
import com.damai.mvc.view.View;
import com.damai.mvc.view.imple.BytesView;
import com.damai.utils.CtClassBuilder;
import com.damai.utils.CtMethodBuilder;
import com.damai.utils.SessionUtil;

import javassist.CannotCompileException;
import javassist.Modifier;
import javassist.NotFoundException;

@Controller(key = "structs")
@Action(WebActionFactory.class)
@Aop(WebInterceptor.class)
public class StructsWebController {

	public void using(Integer index, HttpServletRequest request) {
		request.setAttribute("list", Db.getStruct(index).getTableStructs(DbStructFactory.STRUCT_USING));
	}

	private static final String[] HEADER = "名称,类型,长度,默认值,可空,说明".split(",");
	
	public View exportTable(Integer index,String filter) {
		Collection<TableStruct> structs = Db.getStruct(index==null?0:index).getTableStructs(DbStructFactory.STRUCT_USING);
		ExcelTable table = new ExcelTable();
		table.createSheet("表结构");
		//名称	类型	长度	默认值	可空	说明
		
		
		for (TableStruct tableStruct : structs) {
			if(filter!=null && !tableStruct.getName().toLowerCase().contains(filter)){
				continue;
			}
			Collection<ColumnStruct> columns = tableStruct.getColumns();
			table.addRow(tableStruct.getName() + " " + tableStruct.getComment(),HEADER.length);
			table.addRow(HEADER);
			//名称,类型,长度,默认值,可空,说明
			for (ColumnStruct columnStruct : columns) {
				
				table.addRow(Lang.toArray(columnStruct.getName(),
						columnStruct.getRawType(),
						String.valueOf(columnStruct.getMaxLen()),
						columnStruct.getDefaultValue(),
						columnStruct.isNullable() ? "是": "否",
						columnStruct.getComment()));
			}
		}
		
		
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			table.write(outputStream);
			return new BytesView(outputStream.toByteArray(), "表结构.xls");
		} catch (IOException e) {
			throw new ApiError(e.getMessage());
		}
		
		
		
	}

	public void reactNative(String api, HttpServletRequest request) {
		ApiMetaData data = ApiFilter.me().getApi(api);

		request.setAttribute("name", data.getName());
		request.setAttribute("comment", data.getComment());
		request.setAttribute("params", data.getParams());

		request.setAttribute("crypt", data.getCrypt());

		List<ParamInfo> list = new ArrayList<ParamInfo>();
		for (ParamInfo param : data.getParams()) {
			if (ParamHelperTypes.select.equals(param.getHelperType())) {
				list.add(param);
			}
		}

		request.setAttribute("selects", list);

	}

	@Action(SimpleJsonApiFactory.class)
	public Boolean setting(HttpServletRequest request, String account, String pwd) {
		HttpSession session = request.getSession();
		session.setAttribute("account", account);
		session.setAttribute("pwd", pwd);
		return Boolean.TRUE;
	}

	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public boolean byteCodeReset(String key) {
		ApiFilter.me().resetBackup(key);
		return true;
	}

	@SuppressWarnings("unchecked")
	@Token
	@ReturnString
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public String addTask(String cron, String className) {
		Class<? extends Job> clazz = (Class<? extends Job>) cachedClass.get(className);
		try {
			QuartzPlugin.me().startJob(clazz, cron);
			return "ok";
		} catch (SchedulerException e) {
			return getResponse(e);
		}
	}

	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public Set<String> getTaskNames() {
		return QuartzPlugin.me().getJobNames();
	}

	@SuppressWarnings("unchecked")
	@ReturnString
	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public String removeTask(String className) {
		try {
			Class<? extends Job> clazz = (Class<? extends Job>) cachedClass.get(className);
			QuartzPlugin.me().stopJob(clazz.getSimpleName());
			return "ok";
		} catch (SchedulerException e) {
			return getResponse(e);
		}
	}

	@ReturnString
	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public String getTaskQueueJob(String className) {

		try {
			Class<?> clazz = Class.forName(className);
			JobHandler<?> handler = TaskQueue.getInstance().getHandler(clazz);
			return handler.getClass().getName();
		} catch (ClassNotFoundException e) {
			return "ClassNotFoundException";
		}

	}

	private static Map<String, Class<?>> cachedClass = new HashMap<String, Class<?>>();

	@ReturnString
	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public String createClass(String className, List<String> fields, String superClassName, String contructor,
			String interfaceName, Map<String, String> bodys) {
		Class<?> interfaceClass = null;
		if (interfaceName != null) {
			try {
				interfaceClass = Class.forName(interfaceName);
			} catch (ClassNotFoundException e) {
				return getResponse(e);
			}
		}

		Class<?> superClass = null;
		if (superClassName != null) {
			try {
				superClass = Class.forName(superClassName);
			} catch (ClassNotFoundException e) {
				return getResponse(e);
			}
		}

		try {
			CtClassBuilder builder = new CtClassBuilder(className);
			if (superClass != null) {
				builder.setSuperClass(superClass);
				// 搜索本来的
				Method[] methods = superClass.getDeclaredMethods();

				for (Method method : methods) {
					if (bodys.containsKey(method.getName())) {
						com.damai.utils.CtMethodBuilder methodBuilder = builder.beginMethod(method);
						methodBuilder.setBody(bodys.get(method.getName()));
						bodys.remove(method.getName());
						builder.endMethod();
					}
				}

			}
			if (fields != null) {
				for (String field : fields) {
					builder.addField(field,Modifier.PUBLIC);
				}
			}
			if (contructor != null) {
				builder.addContructor(contructor);
			}

			if (interfaceClass != null) {
				builder.beginInterface(interfaceClass);
				Method[] methods = interfaceClass.getMethods();
				for (Method method : methods) {
					com.damai.utils.CtMethodBuilder methodBuilder = builder.beginMethod(method);
					methodBuilder.setBody(bodys.get(method.getName()));
					bodys.remove(method.getName());
					builder.endMethod();
				}
			}

			Class<?> result = builder.make().getRealClass();
			result.newInstance();
			cachedClass.put(className, result);
			return "ok";
		} catch (Exception e) {
			return getResponse(e);
		}

	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@ReturnString
	@Token
	@Action(ApiJsonActionFactory.class)
	@Crypt(Crypt.BOTH)
	public String addTaskQueueJob(String className, String body) {

		try {
			java.lang.String name = "TaskQueueJob" + java.lang.System.currentTimeMillis();
			com.damai.utils.CtClassBuilder builder = new com.damai.utils.CtClassBuilder(name);
			builder.beginInterface(org.citywithincity.jobqueue.JobHandler.class);

			com.damai.utils.CtMethodBuilder methodBuilder = builder
					.beginMethod(org.citywithincity.jobqueue.JobHandler.class.getMethods()[0]);
			methodBuilder.setBody(body);
			builder.endMethod();

			org.citywithincity.jobqueue.JobHandler handler = builder.make().newInstance();
			Class<?> clazz = Class.forName(className);
			org.citywithincity.jobqueue.TaskQueue.getInstance().setHandler(clazz, handler);

			return "ok";
		} catch (Exception e) {

			return getResponse(e);
		}

	}

	private static String getResponse(Exception e) {
		// e.printStackTrace();
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		PrintStream stream = new PrintStream(outputStream);
		e.printStackTrace(stream);
		stream.close();
		try {
			return new String(outputStream.toByteArray(), "UTF-8");
		} catch (UnsupportedEncodingException e1) {
			return "Not support UnsupportedEncodingException";
		}
	}

	@Token
	@Crypt(Crypt.BOTH)
	@Action(ApiJsonActionFactory.class)
	public boolean jsReplace(String key, String content) {
		Invoker invoker = new JsInvoker(content);
		ApiFilter.me().replaceAndBackup(key, invoker);
		return true;
	}

	@Token
	@Crypt(Crypt.BOTH)
	@Action(ApiJsonActionFactory.class)
	public boolean byteCodeReplace(String content, String key, Customer customer)
			throws NotFoundException, InstantiationException, IllegalAccessException, CannotCompileException {
		// 上传内容，并编译为字节码，并替换
		CtClassBuilder builder = new CtClassBuilder("HotReplace" + key.replace("/", "_") + System.currentTimeMillis());
		builder.beginInterface(Invoker.class);

		CtMethodBuilder methodBuilder = builder.beginMethod(Invoker.class.getMethods()[0]);
		methodBuilder.setBody(content);

		Invoker invoker = builder.endMethod().make().newInstance();

		ApiFilter.me().replaceAndBackup(key, invoker);

		return true;
	}

	@Token
	public boolean byteCodeAdd(String content, String key, Customer customer)
			throws NotFoundException, InstantiationException, IllegalAccessException, CannotCompileException {
		// 上传内容，并编译为字节码，并替换
		CtClassBuilder builder = new CtClassBuilder("key" + System.currentTimeMillis());
		builder.beginInterface(Invoker.class);

		CtMethodBuilder methodBuilder = builder.beginMethod(Invoker.class.getMethods()[0]);
		methodBuilder.setBody(content);

		Invoker invoker = builder.endMethod().make().newInstance();

		ApiFilter.me().replaceAndBackup(key, invoker);

		return true;
	}
	
	/**
	 * 导出接口
	 * @return
	 */
	public View exportApi(String filter){
		List<ApiMetaData> list = ApiUtil.getUsingApi();
		ExcelTable table = new ExcelTable();
		table.createSheet("接口");
		
		
		int COUNT = 2;
		
		//接口名称
		//名称、类型、说明
		for (ApiMetaData api : list) {
			if(filter!=null && !api.getName().contains(filter)){
				continue;
			}
			table.addRow(api.getComment(), COUNT);
			table.addRow("接口:"+api.getName() , COUNT);
			table.addRow("", COUNT);
			if(api.getParams().size() > 0 ){
				table.addRow("参数:",3);
				table.addRow(Lang.toArray("名称","类型","说明"));
				for (ParamInfo param : api.getParams()) {
					table.addRow(Lang.toArray(param.getName(),param.getType(),param.getComment()));
				}
			}else{
				table.addRow("参数:无",COUNT);
			}
			
			if(api.getResults()!=null && api.getResults().size() > 0 ){
				table.addRow("返回数据:",COUNT);
				table.addRow(Lang.toArray("名称","类型","说明"));
				for (ParamInfo param : api.getResults()) {
					table.addRow(Lang.toArray(param.getName(),param.getType(),param.getComment()));
				}
			}else{
				table.addRow("返回数据:"+api.getReturnType(),COUNT);
			}
			
			table.addRow("",COUNT);
			
		}
		

		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		try {
			table.write(outputStream);
			return new BytesView(outputStream.toByteArray(), "接口.xls");
		} catch (IOException e) {
			throw new ApiError(e.getMessage());
		}
	}
	

	/**
	 * api列表
	 */
	public void api(HttpServletRequest request) {
		List<ApiMetaData> list = ApiUtil.getUsingApi();
		HttpSession session = request.getSession();
		request.setAttribute("account", SessionUtil.getString(session, "account", "18659210057"));
		request.setAttribute("pwd", SessionUtil.getString(session, "pwd", "123456"));
		request.setAttribute("count", list.size());
		request.setAttribute("list", list);
	}

	public void table(String table, Integer index, HttpServletRequest request) {
		request.setAttribute("table", Db.getStruct(index).getTableStruct(table));
		request.setAttribute("index", index);

	}

	public void edit_ok(Map<String, String> json, Integer index, HttpServletRequest request) {
		String tableName = (String) json.get("name");
		String comment = (String) json.get("comment");

		Map<String, String> columns = new HashMap<String, String>();
		for (Entry<String, String> entry : json.entrySet()) {

			String key = entry.getKey();
			if (key.startsWith("c.")) {
				columns.put(key.substring(2), entry.getValue());
			}

		}
		TableStruct struct = Db.getStruct(index).getTableStruct(tableName);
		for (ColumnStruct columnStruct : struct.getColumns()) {
			columnStruct.setComment(columns.get(columnStruct.getName()));
		}

		List<String> list = new ArrayList<String>();
		list.add(OracleSqlHelper.alterTableComment(tableName, comment));

		for (Entry<String, String> entry : columns.entrySet()) {

			list.add(OracleSqlHelper.alertColumnComment(tableName, entry.getKey(), entry.getValue()));
		}

		request.setAttribute("list", list);
	}

	private App app;

	@ReturnString
	@Token
	@Crypt(Crypt.BOTH)
	@Action(ApiJsonActionFactory.class)
	public synchronized String hot(String url) {
		if (app != null) {
			app.stop();
		}
		try {
			app = new App(new URLClassLoader(new URL[] { new URL(url) }, StructsWebController.class.getClassLoader()));
			app.start();
			return "Success";
		} catch (MalformedURLException e) {
			return ErrorUtil.printException(e);
		} catch (HotLoadException e) {
			return ErrorUtil.printException(e);
		}
	}

}
